import base64
import io
import json
import math
import os
import random
import uuid
from datetime import datetime
from typing import Optional, Union

from PIL import Image
from enum import Enum


class BaseConfig:
    LAT: float = 29.7276  # 20L附近 # 29.7082 # 02L附近  # 随机的起始经纬度
    LON: float = 106.645  # 20L附近 # 106.638  # 02L附近 随机的起始经纬度
    RADIUS: float = 0.5  # 随机生成范围  单位 KM
    MsgNumSp: int = 1  # 单次发送数量, 每次发送位置都会发生变化
    MsgNumLo: int = 1  # 单次发送数量, 每次发送位置都不会发生变化
    CYCLE: int = 30  # 发送周期


def random_track_id() -> str:
    return ''.join(str(random.randint(0, 9)) for _ in range(18))


class BirdType(Enum):
    """
    用于表示不同鸟类类型的枚举类。
    枚举成员使用英文变量名，对应的值为中文鸟类名称，方便在代码中进行鸟类类型的标识和转换。
    """

    SPARROW = "麻雀"
    MAGPIE = "喜鹊"
    WOODPECKER = "啄木鸟"
    EAGLE = "老鹰"
    OWL = "猫头鹰"
    PIGEON = "鸽子"
    PARROT = "鹦鹉"
    ORIOLE = "黄鹂"
    EGRET = "白鹭"
    WILD_GOOSE = "大雁"
    RAPTOR = "猛禽"

    def __str__(self):
        """
        重写__str__方法，使得在打印该枚举实例或者将其转换为字符串时，输出对应的中文鸟类名称，方便展示和使用。

        :return: 对应的中文鸟类名称。
        """
        return self.value

    @classmethod
    def from_string(cls, bird_type_str):
        """
        根据输入的表示鸟类类型的字符串获取对应的BirdType枚举实例。

        :param bird_type_str: 表示鸟类类型的字符串。
        :return: 对应的BirdType枚举实例。
        :raises ValueError: 如果输入的字符串对应的枚举实例不存在，则抛出此异常。
        """
        try:
            return cls[bird_type_str.upper()]
        except KeyError:
            raise ValueError(f"无效的鸟类类型字符串: {bird_type_str}")

    @classmethod
    def get_all_bird_types(cls):
        """
        获取所有已定义的鸟类类型枚举实例，返回一个列表，方便在需要遍历所有鸟类类型等场景下使用。

        :return: 包含所有BirdType枚举实例的列表。
        """
        return list(cls)

    @classmethod
    def get_random_image(cls, bird_type):
        """
        根据鸟类类型从对应的子文件夹中随机获取一张图像资源。

        :param bird_type: BirdType 枚举实例或表示鸟类类型的字符串。
        :return: PIL Image 对象，如果找不到图像资源返回 None。
        """
        if isinstance(bird_type, str):
            try:
                bird_type = cls.from_string(bird_type)
            except ValueError:
                return None
        # 构建图像资源的路径
        image_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            "resources",
            "images",
            bird_type.name.lower(),
        )
        if os.path.exists(image_folder):
            # 获取文件夹中的所有文件
            image_files = [
                os.path.join(image_folder, f)
                for f in os.listdir(image_folder)
                if os.path.isfile(os.path.join(image_folder, f))
            ]
            if image_files:
                # 随机选择一个文件
                random_image_file = random.choice(image_files)
                try:
                    return Image.open(random_image_file)
                except Exception as e:
                    print(f"打开图像时出错: {e}")
                    return None
            else:
                print(f"文件夹 {image_folder} 中没有图像文件")
                return None
        else:
            print(f"图像资源文件夹不存在: {image_folder}")
            return None

    @classmethod
    def get_random_image_base64(cls, bird_type):
        """
        根据鸟类类型从对应的子文件夹中随机获取一张图像资源，并将其转换为 base64 编码的字符串。

        :param bird_type: BirdType 枚举实例或表示鸟类类型的字符串。
        :return: base64 编码的图像字符串，如果找不到图像资源返回 None。
        """
        print(bird_type.name.upper())
        if isinstance(bird_type, str):
            try:
                bird_type = cls.from_string(bird_type)
            except ValueError:
                return None
        # 构建图像资源的路径
        image_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            "resources",
            "images",
            bird_type.name.lower(),
        )
        print(image_folder)
        if os.path.exists(image_folder):
            # 获取文件夹中的所有文件
            image_files = [
                os.path.join(image_folder, f)
                for f in os.listdir(image_folder)
                if os.path.isfile(os.path.join(image_folder, f))
            ]
            if image_files:
                # 随机选择一个文件
                random_image_file = random.choice(image_files)
                try:
                    with Image.open(random_image_file) as img:
                        buffer = io.BytesIO()
                        if random_image_file.lower().endswith(".png"):
                            img.save(buffer, format="PNG")
                        elif random_image_file.lower().endswith(".jpg"):
                            img.save(buffer, format="JPEG")  # 保存图像到 BytesIO 对象中
                        img_byte_data = buffer.getvalue()  # 获取图像的二进制数据
                        base64_str = base64.b64encode(img_byte_data).decode(
                            "utf-8"
                        )  # 编码为 base64 字符串
                        return base64_str
                except Exception as e:
                    print(f"处理图像时出错: {e}")
                    return None
            else:
                print(f"文件夹 {image_folder} 中没有图像文件")
                return None
        else:
            print(f"图像资源文件夹不存在: {image_folder}")
            return None


class BirdThreat(Enum):
    LowThreshold = 0
    NormalThreshold = 1
    HighThreshold = 2

    def __str__(self):
        return self.value


def template_bird(
        track_id: str,
        timestamp: str,
        longitude: float,
        latitude: float,
        altitude: float = 126.97,
        bird_type: Union[str, BirdType] = "猛禽",
        imgs: Optional[list[str]] = None,
        threat: BirdThreat = BirdThreat.HighThreshold,
):
    """
    根据给定的参数生成一个表示鸟类相关信息的字典。
    @param track_id:
    @param timestamp: 时间戳，格式为字符串，例如 "2023-12-19 17:34:44"。
    @param longitude: 经度信息
    @param latitude: 纬度信息
    @param altitude: 海拔高度信息，默认值为 "10"。
    @param bird_type: 鸟类类型，可以是字符串（会尝试转换为BirdType枚举实例）或者直接传入BirdType枚举实例，默认值为 "猛禽"。
    @param imgs: 鸟类相关图片的Base64编码字符串列表，默认为None。
    @param threat:
    :return: 包含鸟类相关信息的字典。
    """
    if imgs is None:
        imgs = []
    elif not isinstance(imgs, list):
        raise TypeError("imgs参数应该是一个列表类型")
    # 如果bird_type是字符串，尝试转换为BirdType枚举实例
    if isinstance(bird_type, str):
        try:
            bird_type = BirdType.from_string(bird_type)
        except ValueError as e:
            raise ValueError(
                f"无效的鸟类类型字符串: {bird_type}，请传入正确的鸟类类型名称"
            ) from e
    __data = {
        "trackId": track_id,
        "timestamp": timestamp,
        "latitude": latitude,
        "longitude": longitude,
        "altitude": altitude,
        "classification": str(bird_type),  # 将BirdType枚举实例转换为字符串存储
        "num": len(imgs),
        "freq": 10,
        "v": 12,
        "threat": threat.value,
        "conflict": 35,
        "deviceId": str(uuid.uuid4()),
        "imgs": imgs,
    }
    return __data


def build_sms(msg_sms: list, msg_star: str = "ZCZC", msg_end: str = "NNNN") -> str:
    __msg = {"code": "0", "msg": "成功", "data": msg_sms}
    if msg_end and msg_end:
        return msg_star + json.dumps(__msg) + msg_end
    if not (msg_end and msg_end):
        return json.dumps(__msg)
    else:
        return "报头报尾不全"


def jpg_to_base64(jpg_path):
    """
    将指定路径的JPG图片转换为Base64字符串
    :param jpg_path: JPG图片文件的路径
    :return: 对应的Base64字符串
    """
    with open(jpg_path, "rb") as image_file:
        image_data = image_file.read()
    # 使用BytesIO将二进制数据包装成文件对象，便于后续使用PIL库处理
    image = Image.open(io.BytesIO(image_data))
    # 创建一个BytesIO对象，用于临时存储编码后的Base64数据
    buffer = io.BytesIO()
    # 将图片以JPEG格式保存到buffer中，这里可以根据实际图片格式调整，比如如果是PNG图片可改为image.save(buffer, 'PNG')
    if jpg_path.endswith(".jpg"):
        image.save(buffer, format="JPEG")
    elif jpg_path.endswith(".png"):
        image.save(buffer, format="PNG")
    # 获取buffer中的二进制数据
    img_byte_data = buffer.getvalue()
    # 进行Base64编码，将二进制数据转换为Base64字符串
    base64_str = base64.b64encode(img_byte_data).decode("utf-8")
    return base64_str


def generate_random_lat_lon(
        center_lat: float, center_lon: float, radius_km: float, decimal_places: int = 7
):
    """
    在给定的中心点经纬度及半径范围内生成随机的经纬度坐标，并限制经纬度的小数位数。
    @param center_lat: 中心点的纬度
    @param center_lon: 中心点的纬度
    @param radius_km: 中心点的纬度
    @param decimal_places: 中心点的纬度
    @return: 包含随机生成且限制了小数位数的纬度和经度的元组 (random_lat, random_lon)
    """
    # 地球半径，单位千米
    earth_radius_km = 6371.0

    # 将角度转换为弧度
    center_lat_rad = math.radians(center_lat)
    center_lon_rad = math.radians(center_lon)

    # 随机生成一个在半径范围内的距离（单位千米）
    random_distance = random.uniform(0, radius_km)

    # 随机生成一个方向角度（单位弧度）
    random_angle = random.uniform(0, 2 * math.pi)

    # 根据距离和角度计算纬度变化量（单位弧度）
    lat_change_rad = random_distance / earth_radius_km
    # 根据距离、角度以及中心点纬度计算经度变化量（单位弧度）
    lon_change_rad = random_distance / (earth_radius_km * math.cos(center_lat_rad))

    # 计算随机的纬度（单位弧度），再转换回角度
    random_lat_rad = center_lat_rad + lat_change_rad * math.sin(random_angle)
    random_lat = math.degrees(random_lat_rad)

    # 计算随机的经度（单位弧度），再转换回角度
    random_lon_rad = center_lon_rad + lon_change_rad * math.cos(random_angle)
    random_lon = math.degrees(random_lon_rad)

    # 使用字符串格式化来限制经纬度的小数位数
    random_lat = round(random_lat, decimal_places)
    random_lon = round(random_lon, decimal_places)

    return random_lat, random_lon


def formatting_time(time, format_str="%Y-%m-%d %H:%M:%S"):
    """
    将给定的时间数据按照指定格式进行格式化。

    参数:
    time: 可以是时间戳（整数或浮点数，代表从1970年1月1日0时0分0秒 UTC到指定时间的秒数，浮点数可包含毫秒等更精确的时间部分）、datetime对象或者符合特定格式能被strptime解析的字符串。
    format_str: 用于格式化输出的时间格式字符串，默认为 "%Y-%m-%d %H:%M:%S"，例如可以根据需求改成 "%Y-%m-%d" 等其他格式。

    返回:
    格式化后的时间字符串，如果输入的时间数据无法正确解析或格式化，则可能返回None。
    """
    if isinstance(time, (int, float)):
        try:
            # 将时间戳转换为datetime对象
            dt = datetime.fromtimestamp(time)
        except ValueError:
            return None
    elif isinstance(time, datetime):
        dt = time
    elif isinstance(time, str):
        try:
            # 尝试从字符串解析为datetime对象
            dt = datetime.strptime(time, format_str)
        except ValueError:
            return None
    else:
        return None

    # 使用指定格式将datetime对象格式化为字符串
    return dt.strftime(format_str)

if __name__ == '__main__':
    bird = BirdType.SPARROW
    print(bird)
    base64_str = bird.get_random_image_base64(bird)
    print(base64_str)

    b = BirdType.SPARROW
    print(b)
